package com.gitee.magic.context.converter;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.magic.context.ConverterEditorUtils;
import com.gitee.magic.core.annotations.Column;
import com.gitee.magic.core.annotations.Transient;
import com.gitee.magic.core.converter.AbstractConverterEditor;
import com.gitee.magic.core.exception.ApplicationException;
import com.gitee.magic.core.json.JsonObject;
import com.gitee.magic.core.utils.ReflectUtils;
import com.gitee.magic.core.valid.ValidContext;
import com.gitee.magic.core.valid.annotation.NotNull;

/**
 * @author start
 */
public class ObjectConverterEditor extends AbstractConverterEditor<String> {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(ObjectConverterEditor.class);

	private Type typeReference;
	
	public ObjectConverterEditor(Class<?> prototype) {
		super(prototype);
	}
	
	public void setGeneric(Type genericType,Type typeReference) {
		this.typeReference=typeReference;
	}

	@Override
	public void restore(Object value) {
		if (value != null) {
			Class<?> target=value.getClass();
			JsonObject json;
			if(target.equals(JsonObject.class)) {
				json=(JsonObject)value;
			}else {
				json=new JsonObject(String.valueOf(value));
			}
			setSource(json);
			Object instance=ReflectUtils.newInstance(getPrototype());
			Class<?> prototype = getPrototype();
			do {
				Field[] fields = prototype.getDeclaredFields();
				for (Field field : fields) {
					if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {
						continue;
					}
					if (field.isAnnotationPresent(Transient.class)) {
						continue;
					}
					String fieldName = field.getName();
					PropertyDescriptor pd = null;
					try {
						pd = new PropertyDescriptor(fieldName, prototype);
					} catch (IntrospectionException e) {
						if (LOGGER.isErrorEnabled()) {
							LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法", prototype, field.getType(),
									fieldName);
						}
						throw new ApplicationException(e);
					}
					// 别名
					if (field.isAnnotationPresent(Column.class)) {
						Column alias = field.getAnnotation(Column.class);
						fieldName = alias.value();
					}
					if (LOGGER.isDebugEnabled()) {
						LOGGER.debug("JSON字段名{}，类字段名：{}", fieldName, field.getName());
					}
					Object val = null;
					// 判断是否存在当前KeyValue对
					if (json.has(fieldName)) {
						if (json.isNull(fieldName)) {
							ValidContext.verify(field.getAnnotation(NotNull.class), null,fieldName);
						} else {
							val = json.get(fieldName);
						}
					}
					boolean isContinue = false;
					if (val == null) {
						try {
							// 获取默认值
							val = pd.getReadMethod().invoke(instance);
						} catch (IllegalAccessException e) {
							throw new ApplicationException(e);
						} catch (IllegalArgumentException e) {
							throw new ApplicationException(e);
						} catch (InvocationTargetException e) {
							throw new ApplicationException(e);
						}
						ValidContext.verify(field.getAnnotation(NotNull.class), val,fieldName);
						if (val == null) {
							// 如果默认值也为null则直接跳过
							continue;
						}
						isContinue = true;
					}
					// 校验
					ValidContext.verifyField(field, val);
					if (isContinue) {
						continue;
					}
					AbstractConverterEditor<?> convertObj = ConverterEditorUtils.getFieldConverterEditor(field,null,typeReference);
					convertObj.restore(val);
					Object targetValue = convertObj.getValue();
					try {
						pd.getWriteMethod().invoke(instance, targetValue);
					} catch (IllegalAccessException e) {
						throw new ApplicationException(e);
					} catch (IllegalArgumentException e) {
						throw new ApplicationException(e);
					} catch (InvocationTargetException e) {
						throw new ApplicationException(e);
					}
				}
			} while (!(prototype = prototype.getSuperclass()).equals(Object.class));
			setValue(instance);
		}
	}

	@Override
	public String converter() {
		if (getSource() == null) {
			return null;
		}
		return getSource().toString();
	}

	@Override
	public void setValue(Object v) {
		super.setValue(v);
		if(getValue()!=null) {
			JsonObject json=new JsonObject();
			Class<?> prototype=getValue().getClass();
			do {
				Field[] fields=prototype.getDeclaredFields();
				for(Field field:fields) {
					if(Modifier.isFinal(field.getModifiers())||Modifier.isStatic(field.getModifiers())) {
						continue;
					}
					if (field.isAnnotationPresent(Transient.class)) {
						continue;
					}
					String fieldName=field.getName();
					PropertyDescriptor pd=null;
					try {
						pd = new PropertyDescriptor(fieldName, prototype);
					} catch (IntrospectionException e) {
						if(LOGGER.isErrorEnabled()) {
							LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法",prototype,field.getType(),fieldName);
						}
						throw new ApplicationException(e);
					}
					//别名
					if(field.isAnnotationPresent(Column.class)) {
						Column alias=field.getAnnotation(Column.class);
						fieldName=alias.value();
					}
					Object value=null;
					try {
						//获取默认值
						value=pd.getReadMethod().invoke(getValue());
					} catch (IllegalAccessException e) {
						throw new ApplicationException(e);
					} catch (IllegalArgumentException e) {
						throw new ApplicationException(e);
					} catch (InvocationTargetException e) {
						throw new ApplicationException(e);
					}
					//NULL直接跳过
					if(value==null) {
						continue;
					}
					AbstractConverterEditor<?> convertEditor =ConverterEditorUtils.getFieldConverterEditor(field,value.getClass(),null);
					convertEditor.setValue(value);
					json.put(fieldName, convertEditor.getSource());
				}
			}while(!(prototype=prototype.getSuperclass()).equals(Object.class));
			setSource(json);
		}
	}

}
